﻿using Microsoft.Win32;
using System.ComponentModel;
using System.Drawing;
using System.Xml.Serialization;

namespace System.Windows.Forms
{
	public partial class ListViewEx : ListView
	{
		private ColumnHeaderExCollection newCols;

		public class ColumnHeaderDropdownArgs : EventArgs
		{
			public int Column { get; set; }
			public Rectangle ColumnBounds { get; set; }
		}

		public event EventHandler<ColumnHeaderDropdownArgs> ColumnHeaderDropdown;
		public event EventHandler<ColumnClickEventArgs> ColumnRightClick;

		/// <summary>
		/// Gets or sets the <see cref="ContextMenuStrip"/> associated with the column headers of this control.
		/// </summary>
		/// <value>
		/// The column header context menu strip.
		/// </value>
		[DefaultValue(null), Category("Behavior")]
		public ContextMenuStrip ColumnContextMenuStrip { get; set; }

		/// <summary>
		/// Gets the collection of all column headers that appear in the control.
		/// </summary>
		/// <returns>A <see cref="T:System.Windows.Forms.ColumnHeaderExCollection" /> that represents the column headers that appear when the <see cref="P:System.Windows.Forms.ListViewEx.View" /> property is set to <see cref="F:System.Windows.Forms.View.Details" />.</returns>
		/// <remarks>The <c>Columns</c> property returns a collection that contains the <see cref="ColumnHeaderEx"/> objects that are displayed in the <see cref="ListViewEx"/> control. <see cref="ColumnHeaderEx"/> objects define the columns that are displayed in the <see cref="ListViewEx"/> control when the <see cref="P:System.Windows.Forms.ListViewEx.View"/> property is set to <see cref="F:System.Windows.Forms.View.Details"/>. Each column is used to display subitem information for each item in the <see cref="ListViewEx"/>. For more information about how to manipulate the items in the collection, see <see cref="ColumnHeaderExCollection"/>.</remarks>
		[Description("Columns"), Localizable(true), MergableProperty(false), Category("Behavior"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[Editor(typeof(ColumnHeaderExCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
		public new ColumnHeaderExCollection Columns
		{
			get { if (newCols == null) newCols = new ColumnHeaderExCollection(this); return newCols; }
		}

		internal ColumnHeaderCollection BaseColumns
		{
			get { return base.Columns; }
		}

		internal IntPtr HeaderHandle
		{
			get { return this.GetHeaderHandle(); }
		}

		internal void GetLVCOLUMN(int colIndex, NativeMethods.LVCOLUMN col)
		{
			NativeMethods.SendMessage(this.Handle, NativeMethods.ListViewMessage.GetColumn, colIndex, col);
		}

		internal void SetLVCOLUMN(int colIndex, NativeMethods.LVCOLUMN col)
		{
			if (NativeMethods.SendMessage(this.Handle, NativeMethods.ListViewMessage.SetColumn, colIndex, col) == IntPtr.Zero)
				throw new Win32Exception();
		}

		internal void SetColumnFormat(int columnIndex, NativeMethods.ListViewColumnFormat fmtFlag, bool on)
		{
			if (((columnIndex < 0) || ((columnIndex >= 0) && (this.Columns == null))) || (columnIndex >= this.Columns.Count))
				throw new ArgumentOutOfRangeException("columnIndex");

			if (base.IsHandleCreated)
			{
				var lvc = new NativeMethods.LVCOLUMN(NativeMethods.ListViewColumMask.Fmt);
				this.GetLVCOLUMN(columnIndex, lvc);
				if (on)
					lvc.Format |= fmtFlag;
				else
					lvc.Format &= (~fmtFlag);
				this.SetLVCOLUMN(columnIndex, lvc);
				this.InvalidateHeader();
			}
		}

		protected virtual void OnColumnHeaderDropdown(ColumnHeaderDropdownArgs e)
		{
			var h = ColumnHeaderDropdown;
			if (h != null)
				h(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.ListView.ColumnClick" /> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.ColumnClickEventArgs" /> that contains the event data.</param>
		protected override void OnColumnClick(ColumnClickEventArgs e)
		{
			if (this.Columns[e.Column].Sortable)
			{
				ListViewColumnSorter sorter = (ListViewColumnSorter)this.ListViewItemSorter;
				if (e.Column == sorter.SortColumn)
				{
					// Reverse the current sort direction for this column.
					sorter.Order = (sorter.Order == SortOrder.Ascending) ? SortOrder.Descending : SortOrder.Ascending;
				}
				else
				{
					// Set the column number that is to be sorted; default to ascending.
					sorter.SortColumn = e.Column;
					sorter.Order = SortOrder.Ascending;
				}

				// Perform the sort with these new sort options.
				this.Sort();
				this.SetSortIcon(sorter.SortColumn, sorter.Order);
				this.Refresh();
			}
			base.OnColumnClick(e);
		}

		protected virtual void OnColumnRightClick(ColumnClickEventArgs e)
		{
			var h = ColumnRightClick;
			if (h != null)
				h(this, e);
		}

		protected override void OnColumnReordered(ColumnReorderedEventArgs e)
		{
			base.OnColumnReordered(e);
		}

		protected override void OnColumnWidthChanged(ColumnWidthChangedEventArgs e)
		{
			base.OnColumnWidthChanged(e);
		}

		private void OnHandleCreated_Columns(EventArgs e)
		{
			for (int i = 0; i < this.Columns.Count; i++)
			{
				if (this.Columns[i].ShowDropDownButton)
					this.SetColumnDropDown(i, true);

				// Not sure if this needs to happen
				if (this.Columns[i].SortOrder != SortOrder.None)
					this.SetSortIcon(i, this.Columns[i].SortOrder);
			}
		}

		private void WmReflectNotify(ref Message m)
		{
			var nm = m.GetLParam<NativeMethods.NMHDR>();
			if (nm.code == (int)NativeMethods.ListViewNotifications.ColumnDropDown)
			{
				var nmlv = m.GetLParam<NativeMethods.NMLISTVIEW>();
				var iCol = nmlv.iSubItem;
				NativeMethods.RECT rc = new NativeMethods.RECT();
				NativeMethods.SendMessage(HeaderHandle, (uint)NativeMethods.HeaderMessage.GetItemDropDownRect, (IntPtr)iCol, ref rc);
				OnColumnHeaderDropdown(new ColumnHeaderDropdownArgs() { Column = iCol, ColumnBounds = rc });
				if (this.Columns[iCol].DropDownContextMenu != null)
					this.Columns[iCol].DropDownContextMenu.Show(this, rc.X, rc.Bottom);
			}
		}

		/// <summary>
		/// Overrides <see cref="M:System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message@)" />.
		/// </summary>
		/// <param name="m">The Windows <see cref="T:System.Windows.Forms.Message" /> to process.</param>
		[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)]
		protected void WndProc_Columns(ref Message m)
		{
			if (m.Msg == 0x7b) // WM_CONTEXTMENU
			{
				int lp = m.LParam.ToInt32();
				Point pt = new Point(((lp << 16) >> 16), lp >> 16);
				IntPtr hHdr = HeaderHandle;
				if (0 != NativeMethods.MapWindowPoints(IntPtr.Zero, hHdr, ref pt, 1))
				{
					var hti = new NativeMethods.HDHITTESTINFO(pt);
					int item = NativeMethods.SendMessage(hHdr, NativeMethods.HeaderMessage.HitTest, 0, hti).ToInt32();
					if (item != -1)
					{
						OnColumnRightClick(new ColumnClickEventArgs(item));
						if (ColumnContextMenuStrip != null)
							ColumnContextMenuStrip.Show(pt);
					}
					System.Diagnostics.Debug.WriteLine(string.Format("ListViewEx:WM_CONTEXTMENU for column header {0} ({1})", item, hti.Flags));
				}
			}
			else if (m.Msg == 0x204E) // WM_NOTIFY
			{
				WmReflectNotify(ref m);
			}
		}
	}
}